package com.jzo2o.foundations.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.model.Result;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.foundations.constants.RedisConstants;
import com.jzo2o.foundations.mapper.*;
import com.jzo2o.foundations.model.domain.*;
import com.jzo2o.foundations.model.dto.request.ServePageQueryReqDTO;
import com.jzo2o.foundations.model.dto.request.ServeUpsertReqDTO;
import com.jzo2o.foundations.model.dto.response.*;
import com.jzo2o.foundations.service.IServeService;
import com.jzo2o.mysql.utils.PageHelperUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ServeServiceImpl extends ServiceImpl<ServeMapper, Serve> implements IServeService {

    @Autowired
    private ServeItemMapper serveItemMapper;
    @Autowired
    private RegionMapper regionMapper;
    @Autowired
    private ServeTypeMapper serveTypeMapper;
    @Autowired
    private ServeMapper serveMapper;
    @Autowired
    private ServeSyncMapper serveSyncMapper;
    @Autowired
    private RestHighLevelClient client;

    //将es客户端放入容器
    @Bean
    public RestHighLevelClient client() {
        return new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.101.68", 9200, "http")));
    }

    @Override
    public PageResult<ServeResDTO> findByPage(ServePageQueryReqDTO dto) {
        return PageHelperUtils.selectPage(dto, () -> baseMapper.queryListByRegionId(dto.getRegionId()));
    }

    @Override
    public List<ServeSimpleResDTO> findServeList(String cityCode, Long serveTypeId, String keyword) {
        //创建请求
        SearchRequest request = new SearchRequest("serve_aggregation");
        //封装查询条件
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();
        if (serveTypeId != null) {
            boolQuery.must(QueryBuilders.termQuery("serve_type_id", serveTypeId));
        }
        if (StringUtils.isNotEmpty(cityCode)) {
            boolQuery.must(QueryBuilders.termQuery("city_code", cityCode));
        }
        if (StringUtils.isNotEmpty(keyword)) {
            boolQuery.must(QueryBuilders.multiMatchQuery(keyword, "serve_item_name", "serve_type_name"));
        }
        request.source().query(boolQuery);
        //封装排序
        request.source().sort("serve_item_sort_num", SortOrder.ASC);
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            List<ServeSimpleResDTO> collect = Arrays.stream(response.getHits().getHits())
                    .map(e -> JsonUtils.toBean(e.getSourceAsString(), ServeSimpleResDTO.class))
                    .collect(Collectors.toList());
            return collect;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public Result addServes(List<ServeUpsertReqDTO> dts) {
        for (ServeUpsertReqDTO dto : dts) {
            //服务项目必须是启用状态才能添加
            ServeItem serveItem = serveItemMapper.selectOne(new LambdaQueryWrapper<ServeItem>()
                    .eq(ServeItem::getId, dto.getServeItemId())
                    .eq(ServeItem::getActiveStatus, 2));
            if (ObjectUtils.isNull(serveItem)) {
                throw new ForbiddenOperationException("添加失败，不存在该项目或该项目未启用");
            }
            //一个服务项目只能在一个区域中添加一次
            Integer count = this.lambdaQuery()
                    .eq(Serve::getRegionId, dto.getRegionId())
                    .eq(Serve::getServeItemId, dto.getServeItemId()).count();
            if (count != 0) {
                throw new ForbiddenOperationException("添加失败，同一区域不能重复添加相同项目");
            }
            //新增服务
            Serve serve = BeanUtil.copyProperties(dto, Serve.class);
            Region region = regionMapper.selectById(dto.getRegionId());
            if (ObjectUtils.isNotEmpty(region)) {
                serve.setCityCode(region.getCityCode());
            }
            this.save(serve);
        }
        return Result.ok();
    }

    @Override
    @CacheEvict(value = RedisConstants.CacheName.SERVE, key = "#id")
    public Result upServePrice(Long id, BigDecimal price) {
        Serve serve = new Serve();
        serve.setId(id);
        serve.setPrice(price);
        return this.updateById(serve) ? Result.ok() : Result.error("修改失败");
    }

    @Override
    public Result deleteById(Long id) {
        //当前状态为草稿状态才可以删除
        Integer count = this.lambdaQuery()
                .eq(Serve::getId, id)
                .eq(Serve::getSaleStatus, 0)
                .count();
        if (count == 0) {
            return Result.error("当前状态为草稿状态才可以删除");
        }
        this.removeById(id);
        return Result.ok();
    }

    @Override
    @Cacheable(value = RedisConstants.CacheName.SERVE, key = "#id")
    public Result onServe(Long id) {
        //区域服务处于非上架状态，status:0,1
        Serve serve = this.lambdaQuery()
                .eq(Serve::getId, id)
                .one();
        if (serve.getSaleStatus() == 2) {
            throw new ForbiddenOperationException("当前状态为上架状态，无法再次上架");
        }
        //服务项目是启用状态
        ServeItem serveItem = serveItemMapper.selectOne(new LambdaQueryWrapper<ServeItem>()
                .eq(ServeItem::getId, serve.getServeItemId())
                .eq(ServeItem::getActiveStatus, 2));
        if (ObjectUtils.isEmpty(serveItem)) {
            throw new ForbiddenOperationException("上架失败，不存在该项目或该项目未启用");
        }
        //满足条件修改状态
        Serve e = new Serve();
        e.setId(id);
        e.setSaleStatus(2);
        this.updateById(e);
        //添加同步serve_sync数据表数据
        addServeSync(id);
        return Result.ok();
    }

    /**
     * 新增服务同步数据
     *
     * @param serveId 服务id
     */
    private void addServeSync(Long serveId) {
        //服务信息
        Serve serve = baseMapper.selectById(serveId);
        //区域信息
        Region region = regionMapper.selectById(serve.getRegionId());
        //服务项信息
        ServeItem serveItem = serveItemMapper.selectById(serve.getServeItemId());
        //服务类型
        ServeType serveType = serveTypeMapper.selectById(serveItem.getServeTypeId());
        ServeSync serveSync = new ServeSync();
        //ServeType
        serveSync.setServeTypeId(serveType.getId());
        serveSync.setServeTypeName(serveType.getName());
        serveSync.setServeTypeIcon(serveType.getServeTypeIcon());
        serveSync.setServeTypeImg(serveType.getImg());
        serveSync.setServeTypeSortNum(serveType.getSortNum());
        //ServeItem
        serveSync.setServeItemId(serveItem.getId());
        serveSync.setServeItemIcon(serveItem.getServeItemIcon());
        serveSync.setServeItemName(serveItem.getName());
        serveSync.setServeItemImg(serveItem.getImg());
        serveSync.setServeItemSortNum(serveItem.getSortNum());
        serveSync.setUnit(serveItem.getUnit());
        serveSync.setDetailImg(serveItem.getDetailImg());
        //Serve
        serveSync.setPrice(serve.getPrice());
        serveSync.setId(serve.getId());
        serveSync.setIsHot(serve.getIsHot());
        //Region
        serveSync.setCityCode(region.getCityCode());
        serveSyncMapper.insert(serveSync);
    }

    @Override
    @CacheEvict(value = RedisConstants.CacheName.SERVE, key = "#id")
    public Result downServe(Long id) {
        //区域服务处于上架状态才能下架
        Integer count = this.lambdaQuery()
                .eq(Serve::getId, id)
                .eq(Serve::getSaleStatus, 2)
                .count();
        if (count == 0) {
            throw new ForbiddenOperationException("当前状态为非上架状态，无法下架");
        }
        Serve serve = new Serve();
        serve.setId(id);
        serve.setSaleStatus(1);
        this.updateById(serve);
        return Result.ok();
    }

    @Override
    @CacheEvict(value = RedisConstants.CacheName.SERVE, key = "#id")
    public Result onHot(Long id) {
        Serve serve = new Serve();
        serve = this.getById(id);
        if (ObjectUtils.isEmpty(serve)) {
            throw new ForbiddenOperationException("该服务不存在");
        }
        clearHotServeCacheAndUpdateHotServe(serve, 1);
        serveSyncMapper.deleteById(id);
        return Result.ok();
    }

    //修改热门时清空缓存
//    @CacheEvict(value = RedisConstants.CacheName.HOT_SERVE, key = "#serve.regionId")
    private void clearHotServeCacheAndUpdateHotServe(Serve serve, Integer isHot) {
        serve.setIsHot(isHot);
        if (isHot == 1) {
            serve.setHotTimeStamp(new Date().getTime());
        }
        this.updateById(serve);
    }

    @Override
    @CacheEvict(value = RedisConstants.CacheName.SERVE, key = "#id")
    public Result offHot(Long id) {
        Serve serve = new Serve();
        serve = this.getById(id);
        if (ObjectUtils.isEmpty(serve)) {
            throw new ForbiddenOperationException("该服务不存在");
        }
        clearHotServeCacheAndUpdateHotServe(serve, 0);
        return Result.ok();
    }

    /**
     * 查询首页服务分类项目列表-小程序端
     *
     * @param regionId
     * @return
     */
    @Override
    @Caching(
            cacheable = {
                    //返回数据为空，则缓存空值30分钟，这样可以避免缓存穿透
                    @Cacheable(value = RedisConstants.CacheName.SERVE_ICON, key = "#regionId",
                            unless = "#result.size() > 0", cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES),

                    //返回值不为空，则永久缓存数据
                    @Cacheable(value = RedisConstants.CacheName.SERVE_ICON, key = "#regionId",
                            unless = "#result.size() == 0", cacheManager = RedisConstants.CacheManager.FOREVER)
                    //unless的结果为fale则执行
            }
    )
    public List<ServeCategoryResDTO> indexServeList(Long regionId) {
        // 判断当前区域是否启用
        Region region = regionMapper.selectOne(new LambdaQueryWrapper<Region>()
                .eq(Region::getId, regionId)
                .eq(Region::getActiveStatus, 2));
        if (ObjectUtils.isEmpty(region)) {
            throw new ForbiddenOperationException("当前区域未启用");
        }
        // 查询当前区域下可用的服务项目及分类信息
        List<ServeCategoryResDTO> resDTOS = serveMapper.indexServeList(regionId);
        if (CollUtils.isEmpty(resDTOS)) {
            return CollUtils.emptyList();
        }
        // 拼装结果
        List<ServeCategoryResDTO> list = CollUtils.sub(resDTOS, 0, Math.min(resDTOS.size(), 2));
        list.forEach(e -> {
            e.setServeResDTOList(CollUtils.sub(e.getServeResDTOList(), 0, Math.min(e.getServeResDTOList().size(), 4)));
        });
        return list;
    }

    @Override
    //进入首页时查询热门内容并添加至缓存
    @Cacheable(value = RedisConstants.CacheName.HOT_SERVE, key = "#regionId", cacheManager = RedisConstants.CacheManager.FOREVER)
    public List<ServeAggregationSimpleResDTO> hotServeList(Long regionId) {
        List<Serve> list = this.lambdaQuery().eq(Serve::getRegionId, regionId)
                .eq(Serve::getIsHot, 1)
                .eq(Serve::getSaleStatus, 2)
                .orderByDesc(Serve::getUpdateTime).list();
        List<ServeAggregationSimpleResDTO> collect = list.stream().map(e -> {
            ServeItem serveItem = serveItemMapper.selectOne(new LambdaQueryWrapper<ServeItem>()
                    .eq(ServeItem::getId, e.getServeItemId()));
            ServeAggregationSimpleResDTO resDTO = new ServeAggregationSimpleResDTO();
            BeanUtil.copyProperties(e, resDTO);
            resDTO.setServeItemName(serveItem.getName());
            resDTO.setServeItemImg(serveItem.getImg());
            resDTO.setUnit(serveItem.getUnit());
            resDTO.setDetailImg(serveItem.getDetailImg());
            return resDTO;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    @Caching(
            cacheable = {
                    //返回数据为空，则缓存空值30分钟，这样可以避免缓存穿透
                    @Cacheable(value = RedisConstants.CacheName.SERVE, key = "#id",
                            unless = "#result != null", cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES),
                    @Cacheable(value = RedisConstants.CacheName.SERVE, key = "#id",
                            unless = "#result != null", cacheManager = RedisConstants.CacheManager.ONE_DAY)
            }
    )
    public ServeAggregationSimpleResDTO findHotServeInfoById(Long id) {
        if (id == null) {
            throw new ForbiddenOperationException("服务id不能为空");
        }
        Serve serve = this.getById(id);
        ServeItem serveItem = serveItemMapper.selectById(serve.getServeItemId());
        ServeAggregationSimpleResDTO dto = new ServeAggregationSimpleResDTO();
        BeanUtil.copyProperties(serve, dto);
        BeanUtil.copyProperties(serveItem, dto);
        dto.setServeItemName(serveItem.getName());
        dto.setServeItemImg(serveItem.getImg());
        return dto;
    }

    @Override
    public List<ServeAggregationTypeSimpleResDTO> serveTypeList(Long regionId) {
        //查当前区域下所有的服务
        List<Serve> list = this.lambdaQuery().eq(Serve::getRegionId, regionId).list();
        //查这些服务对应的服务类型
        List<Long> serveTypeIds = list.stream().map(e -> {
            ServeItem serveItem = serveItemMapper.selectById(e.getServeItemId());
            return serveItem.getServeTypeId();
        }).distinct().collect(Collectors.toList());
        List<ServeType> selected = serveTypeMapper.selectList(new LambdaQueryWrapper<ServeType>()
                .in(ServeType::getId, serveTypeIds));
        List<ServeAggregationTypeSimpleResDTO> collect = selected.stream().map(e -> {
            ServeAggregationTypeSimpleResDTO res = new ServeAggregationTypeSimpleResDTO();
            res.setServeTypeId(e.getId());
            res.setServeTypeName(e.getName());
            res.setServeTypeImg(e.getImg());
            res.setServeTypeSortNum(e.getSortNum());
            return res;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public ServeAggregationResDTO findServeDetailById(Long id) {
        return baseMapper.findServeDetailById(id);
    }
}
